package com.kingyea.mobilepolice.apimanage.accesscontrol.handle;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.kingyea.mobilepolice.system.exception.BusinessException;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.crypto.*;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author chenjw
 * @version v1.0.0
 * @description
 * @date Created in 2018/4/7 14:23
 */


@Component
public class SecurityService {

    private final static Logger logger = LoggerFactory.getLogger(SecurityService.class);
    private final static String DATA_KEY = "data";
    private final static String ENCODE_KEY = "encodeSecKey";
    /**
     * 存放路径与对应的keyStore
     */
    private final static Map<String, KeyStore> keyStoreMap = Maps.newConcurrentMap();
    /**
     * 信任密钥库
     */
    private String trustStore;
    /**
     * 信任密钥库登录密码
     */
    private String trustStorePassword;
    /**
     * 公钥别名
     */
    private String trustStoreAlias;
    /**
     * 密钥库（私钥）
     */
    private String keyStore;
    /**
     * 密钥库登录密码
     */
    private String keyStorePassword;
    /**
     * 私钥别名
     */
    private String keyStoreAlias;
    /**
     * 私钥密码
     */
    private String keyPassword;
    /**
     * 密钥库类型
     */
    private String keyStoreType = "JKS";
    /**
     * 信任密钥库类型
     */
    private String trustStoreType = "JKS";
    /**
     *
     */
    private String SIGN_ALGORITHMS = "SHA1WithRSA";
    /**
     *
     */
    private String RSA_Algorithm = "RSA";
    /**
     *
     */
    private String DESede_Algorithm = "DESede"; // 定义 加密算法,可用DES,DESede,Blowfish
    private RSAPrivateKey privateKey = null;
    private RSAPublicKey publicKey = null;
    private Cipher cipherRSA = null;

    /**
     * 初始化
     *
     * @throws Exception
     */
    public void init() throws Exception {
        this.publicKey = this.getPublicKey();
        this.privateKey = this.getPrivateKey();
        this.getRSACipher();
        logger.info("======进入了init方法=====");
    }

    private void getRSACipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
        this.cipherRSA = Cipher.getInstance(this.RSA_Algorithm);

    }

    /**
     * 得到公钥
     *
     * @return
     * @throws Exception
     */
    public RSAPublicKey getPublicKey() throws Exception {
        KeyStore ks;
        FileInputStream fin = null;
        RSAPublicKey rsapublicKey = null;
        try {
            if (keyStoreMap.containsKey(this.trustStore)) {
                ks = keyStoreMap.get(this.trustStore);
            } else {
                ks = KeyStore.getInstance(this.trustStoreType);
                Resource resource = new ClassPathResource(this.trustStore);
                ks.load(resource.getInputStream(), this.trustStorePassword.toCharArray());
                keyStoreMap.put(this.trustStore, ks);
            }
            // 读取公钥
            final Certificate cert = ks.getCertificate(this.trustStoreAlias);
            if (cert != null) {
                rsapublicKey = (RSAPublicKey) cert.getPublicKey();
            }
        } catch (final Exception e) {
            logger.error("获取公钥失败", e);
            throw new Exception(e.getMessage());
        } finally {
            IOUtils.closeQuietly(fin);
        }
        return rsapublicKey;
    }

    /**
     * 得到私钥
     *
     * @return
     * @throws Exception
     */
    public RSAPrivateKey getPrivateKey() throws Exception {
        KeyStore ks;
        FileInputStream fin = null;
        RSAPrivateKey rsaprivateKey = null;
        try {
            if (keyStoreMap.containsKey(this.keyStore)) {
                ks = keyStoreMap.get(this.keyStore);
            } else {
                ks = KeyStore.getInstance(this.keyStoreType);
                Resource resource = new ClassPathResource(this.trustStore);
                ks.load(resource.getInputStream(), this.keyStorePassword.toCharArray());
                keyStoreMap.put(this.keyStore, ks);
            }
            rsaprivateKey = (RSAPrivateKey) ks.getKey(this.keyStoreAlias, this.keyPassword.toCharArray());
        } catch (final Exception e) {
            logger.error("获取私钥失败", e);
            throw new Exception(e.getMessage());
        } finally {
            IOUtils.closeQuietly(fin);
        }
        return rsaprivateKey;
    }

    /**
     * 对string类型的数据用私钥签名
     *
     * @param content
     * @return
     */

    public String sign(final String content) {
        try {
            final PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(this.privateKey.getEncoded());
            ;
            final KeyFactory keyf = KeyFactory.getInstance(this.RSA_Algorithm);
            final PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            final java.security.Signature signature = java.security.Signature.getInstance(this.SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes());
            final byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (final Exception e) {
            logger.error("私钥签名失败！！", e);
        }
        return null;

    }

    /**
     * 签名校验（公钥验证）
     *
     * @param content 原文
     * @param sign
     * @return
     */
    public boolean doCheck(final String content, final String sign) {
        boolean result = false;
        try {
            final KeyFactory keyFactory = KeyFactory.getInstance(this.RSA_Algorithm);
            final PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(this.publicKey.getEncoded()));
            final java.security.Signature signature = java.security.Signature.getInstance(this.SIGN_ALGORITHMS);
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            result = signature.verify(Base64.decode(sign));
        } catch (final Exception e) {
            logger.error("签名校验", e);
        }
        return result;
    }

    /**
     * 随机生成24位byte数组
     *
     * @return
     */
    public byte[] random() {
        final String uid = UUID.randomUUID().toString().replace("-", "");
        final byte[] random = uid.substring(0, 24).getBytes();
        return random;
    }

    /**
     * 公钥加密过程
     *
     * @param
     * @param plainTextData
     * @return
     * @throws Exception
     */
    public byte[] encrypt(final byte[] plainTextData) throws Exception {
        if (this.publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        try {
            // 使用默认RSA
            this.cipherRSA.init(Cipher.ENCRYPT_MODE, this.publicKey);
            if (plainTextData == null) {
                return plainTextData;
            }
            final byte[] output = this.cipherRSA.doFinal(plainTextData);
            return output;
        } catch (final InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (final IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (final BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }

    /**
     * 私钥解密过程
     *
     * @param cipherData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public byte[] decrypt(final byte[] cipherData) throws Exception {
        if (this.privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        try {
            // 使用默认RSA
            this.cipherRSA.init(Cipher.DECRYPT_MODE, this.privateKey);
            if (cipherData == null) {
                return cipherData;
            }
            final byte[] output = this.cipherRSA.doFinal(cipherData);
            return output;
        } catch (final InvalidKeyException e) {
            throw new Exception("解密私钥非法,请检查");
        } catch (final IllegalBlockSizeException e) {
            throw new Exception("密文长度非法");
        } catch (final BadPaddingException e) {
            throw new Exception("密文数据已损坏");
        }
    }

    /**
     * des 对称加密
     *
     * @param key  密钥(要求是24的倍数)
     * @param data 要加密的数据
     * @return
     * @throws Exception
     */
    public byte[] DESencrypt(final byte[] key, final byte[] data) throws Exception {

        final SecretKey deskey = new SecretKeySpec(key, this.DESede_Algorithm);
        // 生成一个实现指定转换的 Cipher 对象。Cipher对象实际完成加解密操作
        final Cipher c = Cipher.getInstance(this.DESede_Algorithm);
        c.init(Cipher.ENCRYPT_MODE, deskey);

        if (data == null) {
            return null;
        }
        return c.doFinal(data);
    }

    /**
     * DES j解密
     *
     * @param key
     * @param data
     * @return
     * @throws Exception
     */
    public byte[] DESdecerypt(final byte[] key, final byte[] data) throws Exception {
        final SecureRandom sr = new SecureRandom();
        final DESedeKeySpec ks = new DESedeKeySpec(key);
        final SecretKeyFactory factroy = SecretKeyFactory.getInstance(this.DESede_Algorithm);
        final SecretKey sk = factroy.generateSecret(ks);
        final Cipher c = Cipher.getInstance(this.DESede_Algorithm);
        c.init(Cipher.DECRYPT_MODE, sk, sr);
        if (data == null) {
            return data;
        }
        return c.doFinal(data);
    }


    /**
     * 对map解密
     *
     * @param map
     * @return
     * @throws Exception
     */
    public Map<String, Object> decryptData(Map<String, Object> map) {
        //得到加密后的安全密钥
        String key = map.get(ENCODE_KEY).toString();
        map.remove(ENCODE_KEY);
        Map jo = null;
        try {
            byte[] deskey = this.decrypt(Base64.decode(key));
            String deskeystr = new String(deskey);
            String data = map.get(DATA_KEY).toString();

            byte[] decData = this.DESdecerypt(deskey, Base64.decode(data));
            String decDataStr = new String(decData);

            jo = JSON.parseObject(decDataStr, Map.class);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("222", "解密失败," + e.getMessage());
        }


        return jo;
    }

    /**
     * 对数据进行加密
     *
     * @param result
     * @return
     */
    public Map<String, Object> encryptData(Object result) throws Exception {
        byte[] key = random();
        String dataStr = JSON.toJSONString(result);
        byte[] encdata = DESencrypt(key, dataStr.getBytes());
        String encdataStr = Base64.encode(encdata);
        String encKey = Base64.encode(key);
        Map<String, Object> map = new HashMap<>();
        map.put(DATA_KEY, encdataStr);
        map.put(ENCODE_KEY, encKey);
        return map;
    }

    /**
     * 对用公钥加密的安全密码，用私钥解密
     *
     * @param
     * @param
     * @return
     * @throws Exception
     */
    public byte[] getDecodeKeyByPri(final byte[] encodeKey) throws Exception {
        final byte[] securityKey = this.decrypt(encodeKey);
        return securityKey;

    }

    // ==== 用于 ssl 连接

    /**
     * 获取客户端密钥库
     *
     * @return
     */
    public KeyManager[] getClientKeyManagers() {
        InputStream is = null;
        try {
            // 获取默认的 X509算法
            final String alg = KeyManagerFactory.getDefaultAlgorithm();
            // 创建密钥管理工厂
            final KeyManagerFactory factory = KeyManagerFactory.getInstance(alg);
            final KeyStore ks;
            if (keyStoreMap.containsKey(this.keyStore)) {
                ks = keyStoreMap.get(this.keyStore);
            } else {
                final File certFile = new File(this.keyStore);
                if (!certFile.exists() || !certFile.isFile()) {
                    return null;
                }
                is = new FileInputStream(certFile);
                // 构建以证书相应格式的证书仓库
                ks = KeyStore.getInstance(this.keyStoreType);
                // 加载证书
                ks.load(is, this.keyStorePassword.toCharArray());
                keyStoreMap.put(this.keyStore, ks);
            }
            factory.init(ks, this.keyPassword.toCharArray());
            final KeyManager[] keyms = factory.getKeyManagers();
            return keyms;
        } catch (final Exception e) {
            logger.error("获取客户端密钥库失败", e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (final IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return null;
    }

    /**
     * 获取客户端信任密钥库
     *
     * @return
     */
    public TrustManager[] getClientTrustManagers() {
        // 读取证书仓库输入流
        InputStream is = null;
        try {
            // 信任仓库的默认算法X509
            final String alg = TrustManagerFactory.getDefaultAlgorithm();
            // 获取信任仓库工厂
            final TrustManagerFactory factory = TrustManagerFactory.getInstance(alg);
            final KeyStore ks;
            if (keyStoreMap.containsKey(this.trustStore)) {
                ks = keyStoreMap.get(this.trustStore);
            } else {
                // 读取信任仓库
                is = new FileInputStream(new File(this.trustStore));
                // 密钥类型
                ks = KeyStore.getInstance(this.trustStoreType);
                // 加载密钥
                ks.load(is, this.trustStorePassword.toCharArray());
                keyStoreMap.put(this.trustStore, ks);
            }

            factory.init(ks);
            final TrustManager[] tms = factory.getTrustManagers();
            return tms;
        } catch (final Exception e) {
            logger.error("获取客户端信任密钥库失败", e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (final IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public String getTrustStore() {
        return this.trustStore;
    }

    public void setTrustStore(final String trustStore) {
        this.trustStore = trustStore;
    }

    public String getTrustStorePassword() {
        return this.trustStorePassword;
    }

    public void setTrustStorePassword(final String trustStorePassword) {
        this.trustStorePassword = trustStorePassword;
    }

    public String getTrustStoreAlias() {
        return this.trustStoreAlias;
    }

    public void setTrustStoreAlias(final String trustStoreAlias) {
        this.trustStoreAlias = trustStoreAlias;
    }

    public String getKeyStore() {
        return this.keyStore;
    }

    public void setKeyStore(final String keyStore) {
        this.keyStore = keyStore;
    }

    public String getKeyStorePassword() {
        return this.keyStorePassword;
    }

    public void setKeyStorePassword(final String keyStorePassword) {
        this.keyStorePassword = keyStorePassword;
    }

    public String getKeyStoreAlias() {
        return this.keyStoreAlias;
    }

    public void setKeyStoreAlias(final String keyStoreAlias) {
        this.keyStoreAlias = keyStoreAlias;
    }

    public String getKeyPassword() {
        return this.keyPassword;
    }

    public void setKeyPassword(final String keyPassword) {
        this.keyPassword = keyPassword;
    }

    public String getKeyStoreType() {
        return this.keyStoreType;
    }

    public void setKeyStoreType(final String keyStoreType) {
        this.keyStoreType = keyStoreType;
    }

    public String getTrustStoreType() {
        return this.trustStoreType;
    }

    public void setTrustStoreType(final String trustStoreType) {
        this.trustStoreType = trustStoreType;
    }

    public String getSIGN_ALGORITHMS() {
        return this.SIGN_ALGORITHMS;
    }

    public void setSIGN_ALGORITHMS(final String sIGN_ALGORITHMS) {
        this.SIGN_ALGORITHMS = sIGN_ALGORITHMS;
    }

    public String getRSA_Algorithm() {
        return this.RSA_Algorithm;
    }

    public void setRSA_Algorithm(final String rSA_Algorithm) {
        this.RSA_Algorithm = rSA_Algorithm;
    }

    public String getDESede_Algorithm() {
        return this.DESede_Algorithm;
    }

    public void setDESede_Algorithm(final String dESede_Algorithm) {
        this.DESede_Algorithm = dESede_Algorithm;
    }


}
